package socket

import (
	"context"
	"device-modbus/internal/biz/dao"
	"device-modbus/internal/biz/model"
	"device-modbus/internal/plugin_manager"
	"device-modbus/plugin"
	"errors"
	"sort"
	"sync"
	"time"

	"github.com/cloudwego/hertz/pkg/common/hlog"
	"github.com/panjf2000/gnet/v2"
)

// Client 客户端结构体，封装所有客户端状态和操作
type Client struct {
	conn            gnet.Conn
	isAuth          bool
	deviceID        string
	iccid           string
	lastPong        time.Time
	lastPingSent    time.Time
	missedPings     int
	ctx             context.Context
	cancel          context.CancelFunc
	remoteAddr      string
	connectedAt     time.Time
	pingTimer       *time.Timer
	device          *model.Device
	lastOfflineTime time.Time
	sendChan        chan []byte
	sendDone        chan struct{}
	//协议类型
	protocolType string
	//采集间隔（秒）
	collectionInterval time.Duration
	//是否开机
	isOn bool
	// 数据采集相关字段
	dataCollectionCfg dao.DataCollectionConfig
	dataBuffer        []string
	dataMutex         sync.RWMutex
	collectorTickers  map[string]*time.Ticker // 每个指令一个定时器
	dataCollectorDone chan struct{}

	//每次数据循环的开始时间 时间戳（秒）
	dataCollectionStartTime int64
	// 细粒度锁，分别保护不同字段组
	connLock      sync.RWMutex // 保护连接相关字段
	authLock      sync.RWMutex // 保护认证相关字段
	heartbeatLock sync.RWMutex // 保护心跳相关字段
	stateLock     sync.RWMutex // 保护状态相关字段
	//保护时间戳（秒）
	timeLock sync.RWMutex // 保护时间相关字段
	// 数据采集相关锁
	dataCollectionLock sync.RWMutex // 保护数据采集相关字段
}

// NewClient 创建新的客户端实例
func NewClient(conn gnet.Conn, sendChanBufferSize int) *Client {
	ctx, cancel := context.WithCancel(context.Background())
	return &Client{
		conn:              conn,
		ctx:               ctx,
		cancel:            cancel,
		remoteAddr:        conn.RemoteAddr().String(),
		connectedAt:       time.Now(),
		lastPong:          time.Now(),
		sendChan:          make(chan []byte, sendChanBufferSize),
		sendDone:          make(chan struct{}),
		dataCollectorDone: make(chan struct{}),
		dataBuffer:        make([]string, 0),
		collectorTickers:  make(map[string]*time.Ticker),
		dataCollectionCfg: dao.DataCollectionConfig{
			Enabled:       false,
			Commands:      make(map[string]dao.DataCollectionCommand),
			MaxBufferSize: 100,
		},
	}
}

// 配置数据采集
func (c *Client) ConfigureDataCollection(cfg dao.DataCollectionConfig) {
	c.dataMutex.Lock()
	defer c.dataMutex.Unlock()
	// 输出cfg
	hlog.Infof("ConfigureDataCollection cfg: %v", cfg)
	// 先停止所有现有定时器
	c.stopAllCollectors()

	// 更新配置
	c.dataCollectionCfg = cfg
	// 采集间隔（秒）
	c.collectionInterval = cfg.Interval
	// 如果启用，启动所有配置的指令采集器
	if cfg.Enabled {
		//根据间隔 启动所有配置的指令采集器
		c.startAllCollectors()
	}

	hlog.Infof("客户端 %s 数据采集配置已更新，指令数量: %d", c.GetRemoteAddr(), len(cfg.Commands))
}

// 更新单个指令配置
func (c *Client) UpdateCommandConfig(command dao.DataCollectionCommand) {
	//c.dataMutex.Lock()
	//defer c.dataMutex.Unlock()
	//
	//// 停止现有同名指令的定时器
	//if ticker, exists := c.collectorTickers[command.Name]; exists {
	//    ticker.Stop()
	//    delete(c.collectorTickers, command.Name)
	//}
	//
	//// 更新或添加指令
	//c.dataCollectionCfg.Commands[command.Name] = command
	//
	//// 如果启用，启动新的定时器
	//if command.Enabled && c.dataCollectionCfg.Enabled {
	//    c.startCollector(command)
	//}

	hlog.Infof("客户端 %s 指令 %s 配置已更新", c.GetRemoteAddr(), command.Name)
}

// 启动所有采集器

// 启动所有采集器：总调度逻辑改为"执行所有命令→等待全部完成→间隔指定时间→重复"
func (c *Client) startAllCollectors() {
	// 如果总间隔<=0，仅执行一次后退出
	if c.collectionInterval <= 0 {
		return
	}
	// 总调度循环：等待所有命令完成后，间隔指定时间再执行下一轮
	go func() {
		defer func() {
			if r := recover(); r != nil {
				hlog.Errorf("客户端 %s 采集调度发生panic: %v", c.GetRemoteAddr(), r)
			}
		}()
		for {
			select {
			case <-c.ctx.Done():
				hlog.Debugf("客户端 %s 上下文已关闭，退出总采集调度", c.GetRemoteAddr())
				return
			case <-c.dataCollectorDone:
				hlog.Debugf("客户端 %s 数据采集已停止，退出总采集调度", c.GetRemoteAddr())
				return
			default:
				// 等待指定间隔时间（期间可被上下文关闭信号打断）
				select {
				case <-time.After(c.collectionInterval):
					// 间隔时间到，执行下一轮采集，间隔时间
					hlog.Debugf("客户端 %s 采集间隔到，执行下一轮采集", c.GetRemoteAddr())
					c.SetDataCollectionStartTime(time.Now().UnixMilli())
					// 执行所有启用的命令
					c.executeAllCommandsAndWait()
				case <-c.ctx.Done():
					hlog.Debugf("客户端 %s 上下文已关闭，退出总采集调度", c.GetRemoteAddr())
					return
				case <-c.dataCollectorDone:
					hlog.Debugf("客户端 %s 数据采集已停止，退出总采集调度", c.GetRemoteAddr())
					return
				}
			}
		}
	}()
}

// GetSortedCommands 获取按 Sort 升序排序的指令列表
func (c *Client) GetSortedCommands() []dao.DataCollectionCommand {
	commands := make([]dao.DataCollectionCommand, 0, len(c.dataCollectionCfg.Commands))
	for _, cmd := range c.dataCollectionCfg.Commands {
		commands = append(commands, cmd)
	}
	// 按 Sort 升序排序
	sort.Slice(commands, func(i, j int) bool {
		return commands[i].Sort < commands[j].Sort
	})
	return commands
}

// 执行所有启用的命令并等待全部完成
func (c *Client) executeAllCommandsAndWait() {
	//var wg sync.WaitGroup // 用于等待所有命令执行完毕

	c.dataCollectionLock.Lock()
	commands := c.GetSortedCommands()
	// 启动所有启用的命令，并注册到等待组
	for _, cmd := range commands {
		if cmd.Enabled && !c.hasTicker(cmd.Name) {
			//wg.Add(1) // 每个命令加1个计数
			// 启动采集器，传入等待组指针用于通知完成
			c.startCollector(cmd, nil)
		}
	}
	c.dataCollectionLock.Unlock()

	// 等待本轮所有命令执行完毕
	//wg.Wait()
	hlog.Debugf("客户端 %s 本轮所有命令执行完毕", c.GetRemoteAddr())
}

// 启动单个指令的采集器（接收WaitGroup用于通知完成）
func (c *Client) startCollector(cmd dao.DataCollectionCommand, wg *sync.WaitGroup) {
	hlog.Infof("启动指令采集器: %v", cmd)

	// 根据命令间隔创建ticker（控制命令执行前的等待时间）
	var ticker *time.Ticker
	if cmd.Interval <= 0 {
		// 间隔<=0时，立即执行
		ticker = time.NewTicker(time.Millisecond)
	} else {
		// 间隔>0时，等待指定时间后执行 按秒计算
		ticker = time.NewTicker(cmd.Interval)
	}

	// 存储ticker（已在调用方加锁）
	c.collectorTickers[cmd.Name] = ticker

	// 启动采集循环
	c.dataCollectionLoop(cmd, ticker, wg)
	hlog.Debugf("客户端 %s 指令 %s 采集器启动，间隔: %v", c.GetRemoteAddr(), cmd.Name, cmd.Interval)
}

// 停止所有采集器
func (c *Client) stopAllCollectors() {
	c.dataCollectionLock.Lock()
	defer c.dataCollectionLock.Unlock()

	for name, ticker := range c.collectorTickers {
		ticker.Stop()
		delete(c.collectorTickers, name)
		hlog.Debugf("客户端 %s 停止指令 %s 采集器", c.GetRemoteAddr(), name)
	}
}

// 数据采集循环 - 只执行一次（等待命令间隔后执行）
func (c *Client) dataCollectionLoop(cmd dao.DataCollectionCommand, ticker *time.Ticker, wg *sync.WaitGroup) {
	// 确保退出时清理资源并通知等待组
	defer func() {
		//c.dataCollectionLock.Lock()
		if t, exists := c.collectorTickers[cmd.Name]; exists && t == ticker {
			ticker.Stop()
			delete(c.collectorTickers, cmd.Name)
			hlog.Debugf("客户端 %s 指令 %s 采集器完成并清理", c.GetRemoteAddr(), cmd.Name)
		}
		//c.dataCollectionLock.Unlock()
		if wg != nil {
			wg.Done() // 通知等待组：当前命令执行完毕
		}
	}()

	// 等待命令间隔时间后执行（只执行一次）
	select {
	case <-ticker.C:
		// 执行指令采集数据
		c.executeCommand(cmd)
		hlog.Debugf("客户端 %s 指令 %s 执行一次采集完成", c.GetRemoteAddr(), cmd.Name)

	case <-c.ctx.Done():
		hlog.Debugf("客户端 %s 上下文已关闭，指令 %s 采集器退出", c.GetRemoteAddr(), cmd.Name)
		return
	case <-c.dataCollectorDone:
		hlog.Debugf("客户端 %s 数据采集已停止，指令 %s 采集器退出", c.GetRemoteAddr(), cmd.Name)
		return
	}
}

// 辅助方法：检查ticker是否存在（避免重复启动）
func (c *Client) hasTicker(name string) bool {
	_, exists := c.collectorTickers[name]
	return exists
}

// 辅助方法：设置ticker（带锁）
func (c *Client) setTicker(name string, ticker *time.Ticker) {
	c.dataCollectionLock.Lock()
	defer c.dataCollectionLock.Unlock()
	c.collectorTickers[name] = ticker
}

// 执行具体指令并处理结果
func (c *Client) executeCommand(cmd dao.DataCollectionCommand) {
	// 输出cmd
	hlog.Infof("executeCommand cmd: %v", cmd)
	globalManager, err := plugin_manager.GetGlobalManager()
	if err != nil {
		hlog.Errorf("获取全局插件管理器失败: %v", err)
		return
	}
	// 获取协议插件
	protocolPlugin, ok := globalManager.GetPlugin(c.protocolType)
	if !ok {
		hlog.Errorf("协议插件 %s 不存在", c.protocolType)
		return
	}
	// 构建请求数据
	req, err := protocolPlugin.BuildRequest(c.ctx, &plugin.BuildRequestRequest{
		Command: cmd.Command,
		Params:  cmd.Params,
	})
	if err != nil {
		hlog.Errorf("协议插件 %s 构建请求失败: %v", c.protocolType, err)
		return
	}
	payload := req.Data
	if err := c.Send(payload); err != nil {
		hlog.Errorf("客户端 %s 发送指令 %s 结果失败: %v", c.GetRemoteAddr(), cmd.Name, err)
	} else {
		hlog.Debugf("客户端 %s 发送指令 %s 成功", c.GetRemoteAddr(), cmd.Name)
	}
}

// 获取当前数据采集配置
func (c *Client) GetDataCollectionConfig() dao.DataCollectionConfig {
	c.dataMutex.RLock()
	defer c.dataMutex.RUnlock()
	return c.dataCollectionCfg
}

// 连接相关方法
func (c *Client) GetConn() gnet.Conn {
	c.connLock.RLock()
	defer c.connLock.RUnlock()
	return c.conn
}

func (c *Client) SetConn(conn gnet.Conn) {
	c.connLock.Lock()
	defer c.connLock.Unlock()
	c.conn = conn
}

func (c *Client) GetRemoteAddr() string {
	c.connLock.RLock()
	defer c.connLock.RUnlock()
	return c.remoteAddr
}

func (c *Client) GetConnectedAt() time.Time {
	c.connLock.RLock()
	defer c.connLock.RUnlock()
	return c.connectedAt
}

// 认证相关方法
func (c *Client) IsAuthenticated() bool {
	c.authLock.RLock()
	defer c.authLock.RUnlock()
	return c.isAuth
}

func (c *Client) SetAuthenticated(auth bool) {
	c.authLock.Lock()
	defer c.authLock.Unlock()
	c.isAuth = auth
}

func (c *Client) GetDeviceID() string {
	c.authLock.RLock()
	defer c.authLock.RUnlock()
	return c.deviceID
}

func (c *Client) SetDeviceID(id string) {
	c.authLock.Lock()
	defer c.authLock.Unlock()
	c.deviceID = id
}

func (c *Client) GetIccid() string {
	c.authLock.RLock()
	defer c.authLock.RUnlock()
	return c.iccid
}

func (c *Client) SetIccid(iccid string) {
	c.authLock.Lock()
	defer c.authLock.Unlock()
	c.iccid = iccid
}

func (c *Client) GetDevice() *model.Device {
	c.authLock.RLock()
	defer c.authLock.RUnlock()
	return c.device
}

func (c *Client) SetDevice(device *model.Device) {
	c.authLock.Lock()
	defer c.authLock.Unlock()
	c.device = device
}

// 继电器状态
func (c *Client) GetIsOn() bool {
	c.stateLock.RLock()
	defer c.stateLock.RUnlock()
	return c.isOn
}

func (c *Client) SetIsOn(isOn bool) {
	c.stateLock.Lock()
	defer c.stateLock.Unlock()
	c.isOn = isOn
}

// 心跳相关方法
func (c *Client) GetLastPong() time.Time {
	c.heartbeatLock.RLock()
	defer c.heartbeatLock.RUnlock()
	return c.lastPong
}

func (c *Client) SetLastPong(t time.Time) {
	c.heartbeatLock.Lock()
	defer c.heartbeatLock.Unlock()
	c.lastPong = t
}

func (c *Client) GetLastPingSent() time.Time {
	c.heartbeatLock.RLock()
	defer c.heartbeatLock.RUnlock()
	return c.lastPingSent
}

func (c *Client) SetLastPingSent(t time.Time) {
	c.heartbeatLock.Lock()
	defer c.heartbeatLock.Unlock()
	c.lastPingSent = t
}

func (c *Client) GetMissedPings() int {
	c.heartbeatLock.RLock()
	defer c.heartbeatLock.RUnlock()
	return c.missedPings
}

func (c *Client) IncrementMissedPings() int {
	c.heartbeatLock.Lock()
	defer c.heartbeatLock.Unlock()
	c.missedPings++
	return c.missedPings
}

func (c *Client) ResetMissedPings() {
	c.heartbeatLock.Lock()
	defer c.heartbeatLock.Unlock()
	c.missedPings = 0
}

func (c *Client) GetPingTimer() *time.Timer {
	c.heartbeatLock.RLock()
	defer c.heartbeatLock.RUnlock()
	return c.pingTimer
}

func (c *Client) SetPingTimer(timer *time.Timer) {
	c.heartbeatLock.Lock()
	defer c.heartbeatLock.Unlock()
	c.pingTimer = timer
}
func (c *Client) SetProtocolType(protocolType string) {
	c.heartbeatLock.Lock()
	defer c.heartbeatLock.Unlock()
	c.protocolType = protocolType
}
func (c *Client) GetProtocolType() string {
	c.heartbeatLock.RLock()
	defer c.heartbeatLock.RUnlock()
	return c.protocolType
}

// 状态相关方法
func (c *Client) GetLastOfflineTime() time.Time {
	c.stateLock.RLock()
	defer c.stateLock.RUnlock()
	return c.lastOfflineTime
}

func (c *Client) SetLastOfflineTime(t time.Time) {
	c.stateLock.Lock()
	defer c.stateLock.Unlock()
	c.lastOfflineTime = t
}

// 发送消息相关方法
func (c *Client) GetSendChan() chan []byte {
	return c.sendChan
}

func (c *Client) GetSendDone() chan struct{} {
	return c.sendDone
}

func (c *Client) GetContext() context.Context {
	return c.ctx
}

func (c *Client) Send(data []byte) error {
	select {
	case c.sendChan <- data:
		return nil
	case <-c.ctx.Done():
		return errors.New("客户端已关闭")
	default:
		return errors.New("发送缓冲区已满")
	}
}

func (c *Client) SyncWrite(data []byte) error {
	c.connLock.Lock()
	defer c.connLock.Unlock()

	if c.conn == nil {
		return errors.New("连接已关闭")
	}

	_, err := c.conn.Write(data)
	return err
}

// 清理客户端资源
func (c *Client) Cleanup() {
	// 停止数据采集
	c.dataMutex.Lock()
	c.stopAllCollectors()
	c.dataMutex.Unlock()

	// 停止定时器
	c.heartbeatLock.Lock()
	if c.pingTimer != nil {
		c.pingTimer.Stop()
		c.pingTimer = nil
	}
	c.heartbeatLock.Unlock()

	// 取消上下文
	c.cancel()

	// 等待发送协程退出
	select {
	case <-c.sendDone:
	case <-time.After(1 * time.Second):
		// 超时由调用方处理日志
	}

	// 关闭连接
	c.connLock.Lock()
	c.conn = nil
	c.connLock.Unlock()
}

func (c *Client) Close() error {
	c.connLock.Lock()
	defer c.connLock.Unlock()

	if c.conn != nil {
		return c.conn.Close()
	}
	return nil
}

func (c *Client) IsClosed() bool {
	select {
	case <-c.ctx.Done():
		return true
	default:
		return false
	}
}

func (c *Client) UpdateHeartbeat(pingInterval, pingResponseTimeout time.Duration) bool {
	now := time.Now()
	c.heartbeatLock.Lock()
	defer c.heartbeatLock.Unlock()

	sinceLastPong := now.Sub(c.lastPong)

	// 如果距离上次Pong的时间超过响应超时，增加丢失计数
	if sinceLastPong > pingResponseTimeout {
		c.missedPings++
	}
	// 检查是否需要发送新的Ping
	//sinceLastPing := now.Sub(c.lastPingSent)
	//if sinceLastPing >= pingInterval {
	//    c.lastPingSent = now
	//    return false // 返回false表示需要发送Ping但不需要关闭连接
	//}

	return false
}

func (c *Client) GetNextHeartbeatInterval(pingInterval time.Duration) time.Duration {
	c.heartbeatLock.RLock()
	defer c.heartbeatLock.RUnlock()

	nextCheck := pingInterval - time.Since(c.lastPingSent)
	if nextCheck < 0 {
		nextCheck = 0
	}
	return nextCheck
}
func (c *Client) GetDataCollectionStartTime() int64 {
	c.timeLock.RLock()
	defer c.timeLock.RUnlock()
	return c.dataCollectionStartTime
}
func (c *Client) SetDataCollectionStartTime(t int64) {
	c.timeLock.Lock()
	defer c.timeLock.Unlock()
	c.dataCollectionStartTime = t
}
